import {
  objectToString,
  objectToStringFormat,
  duLinkedList
} from "../../shared";
import { AbstractList, IListConstructor } from "./list-type";
import { ListNode } from "./list-node";
/**
 * 单链表构造器
 */
export interface DuLinkedIListConstructor<T> extends IListConstructor<T> {
  new (datas: T[]): DuLinkedList<T>;
  readonly [Symbol.species]: DuLinkedIListConstructor<T>;
  readonly prototype: DuLinkedList<T>;
  isDuLinkedList<T>(obj: unknown): obj is DuLinkedList<T>;
  of<T>(...datas: T[]): DuLinkedList<T>;
  from<T>(datas: T[]): DuLinkedList<T>;
}
/**
 * 判断是否是双链表
 * @param obj 任意对象
 */
export function isDuLinkedList<T>(obj: unknown): obj is DuLinkedList<T> {
  return obj instanceof DuLinkedList;
}
/**
 * 双链表
 */
export class DuLinkedList<T> extends AbstractList<T> {
  static [Symbol.hasInstance](instance: unknown): boolean {
    return objectToString(instance) === objectToStringFormat(duLinkedList);
  }
  static from<T>(datas: T[]): DuLinkedList<T> {
    return new DuLinkedList(datas);
  }
  static of<T>(...datas: T[]): DuLinkedList<T> {
    return new DuLinkedList(datas);
  }
  static isDuLinkedList<T>(obj: unknown): obj is DuLinkedList<T> {
    return isDuLinkedList(obj);
  }
  static get [Symbol.species](): DuLinkedIListConstructor<any> {
    return DuLinkedList;
  }
  [Symbol.toStringTag] = duLinkedList;
  constructor(datas: T[] = []) {
    super();
    for (const el of datas) this.add(el);
  }
  add(element: T, index: number = this._size): void {
    this.checkAddRange(index);
    if (index === 0) {
      const next = this._firstNode;
      const node = new ListNode<T>(element, null, next);
      if (next !== null) next.prev = node;
      this._firstNode = node;
    } else {
      for (const [k, v] of this.entriesForNode()) {
        if (k === index - 1) {
          const prev = v;
          const next = v.next;
          const node = new ListNode<T>(element, prev, next);
          if (next !== null) next.prev = node;
          prev.next = node;
          break;
        }
      }
    }
    this._size++;
  }
  delete(index: number): T {
    this.checkRange(index);
    let el = (this._firstNode as ListNode<T>).element;
    if (this._size === 1) {
      this.clear();
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k === index - 1) {
          const node = v.next as ListNode<T>;
          el = node.element;
          v.next = node.next;
          if (node.next !== null) node.next.prev = node.prev;
          break;
        }
      this._size--;
    }
    return el;
  }
}
